# -*- coding: utf-8 -*-
import os
import uuid
import json

import aiofiles
from fastapi import Request, UploadFile, File, Form, BackgroundTasks
from fastapi.responses import FileResponse
from loguru import logger
from pydantic import BaseModel

from schemas.response_entity import JsonModel
from configs import UPLOAD_FILE_DIR
from utils.file_utils import normalize_filename_extension, to_abs_path, to_rel_path
from utils.hash_utils import cal_md5
from db.repository.dual_control_file import db_get_dc_file_by_name, db_add_dc_file, db_get_dc_file_by_id, \
    db_get_dc_files_by_origin_name, db_update_dc_file_delete_state, db_update_dc_file_info, \
    db_get_dc_files_by_user_groups, db_get_dc_files

from .utils import parse_dc_excel_background


# 双控清单Excel上传
# 功能：管理员上传xlsx文件，去重保存并触发后台解析
async def upload_dul_control_excel_file(
        request: Request,
        background: BackgroundTasks,
        token=Form(...),
        file: UploadFile = File(...),  # 必需文件参数放前面
        group_ids: str = Form(None),  # 用户组ID列表，JSON字符串格式
):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()

    # 验证文件类型
    allowed_extensions = {'xlsx', 'XLSX'}
    normalized_filename = normalize_filename_extension(file.filename)
    if '.' in normalized_filename:
        file_suffix = normalized_filename.rsplit('.', 1)[-1]
    else:
        file_suffix = ""

    if file_suffix not in allowed_extensions:
        return JsonModel(code=400, data=None,
                         msg=f"不支持的文件类型: {file_suffix}，仅支持 {', '.join(allowed_extensions)}").to_response()

    # 计算文件的 MD5 值与文件大小
    file_content = await file.read()
    file_md5 = cal_md5(file_content)
    file_hash_name = f"{file_md5}.{file_suffix}"
    file_size = len(file_content)

    file_obj = db_get_dc_file_by_name(file_hash_name)
    if file_obj:
        return JsonModel(code=403, data=None, msg="文件已存在，关联数据库即可").to_response()

    # 文件不存在：保存到本地
    dir_path = os.path.join(UPLOAD_FILE_DIR, file_md5)
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)
    file_hash_path = os.path.join(dir_path, file_hash_name)

    try:
        async with aiofiles.open(file_hash_path, "wb") as f:
            await f.write(file_content)
    except IOError as e:
        logger.error(f"文件保存失败: {str(e)}")
        return JsonModel(code=500, data=None, msg="文件保存失败").to_response()

    # 解析用户组ID
    parsed_group_ids = []
    if group_ids:
        try:
            parsed_group_ids = json.loads(group_ids)
            if not isinstance(parsed_group_ids, list):
                parsed_group_ids = []
        except (json.JSONDecodeError, TypeError):
            logger.warning(f"Invalid group_ids format: {group_ids}")
            parsed_group_ids = []

    file_obj, is_delete = db_add_dc_file(user.id, file_hash_name, normalized_filename, file_hash_path, file_suffix, file_size,
                                         parsed_group_ids)
    background.add_task(parse_dc_excel_background, file_obj, file_hash_path)
    logger.info(f"Scheduled background parsing for {normalized_filename} at {file_hash_path}")
    return JsonModel(code=200, data={"id": str(file_obj.id)},
                     msg=f"上传文件:{normalized_filename}成功，已开始后台解析").to_response()


# 按名称搜索双控清单文件
# 功能：管理员按原始文件名模糊查询
async def get_dc_files_by_name(request: Request, filename: str = ""):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        files = db_get_dc_files_by_origin_name(filename)
        return JsonModel(code=200, data=[{
            "id": str(f.id),
            "name": f.name,
            "size": f.size,
            "type": f.type,
            "group_ids": f.group_ids,
            "create_time": f.create_time.strftime("%Y-%m-%d %H:%M:%S"),
            "updated_time": f.updated_time.strftime("%Y-%m-%d %H:%M:%S"),
        } for f in files], msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


# 获取双控清单文件详情
# 功能：管理员按ID查询文件详情
async def get_dc_file_by_id(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        f = db_get_dc_file_by_id(file_id)
        if not f or f.is_delete:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data={
            "id": str(f.id),
            "name": f.name,
            "size": f.size,
            "type": f.type,
            "group_ids": f.group_ids,
            "create_time": f.create_time.strftime("%Y-%m-%d %H:%M:%S"),
            "updated_time": f.updated_time.strftime("%Y-%m-%d %H:%M:%S"),
        }, msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


# 查询双控清单文件列表
# 功能：超级管理员查看全部，其余按用户组过滤
async def list_dc_files(request: Request, page: int = 1, page_size: int = 50, name: str = ""):
    user = request.state.user
    try:
        offset = (page - 1) * page_size
        if user.check_superman_role():
            files, total = db_get_dc_files(name_like=name or None, offset=offset, limit=page_size)
        else:
            # 非超级管理员只能查看该用户所在组的可查看文件
            from db.repository.user import db_get_user_group_ids
            user_group_ids = db_get_user_group_ids(user)
            files, total = db_get_dc_files_by_user_groups(user_group_ids, name_like=name or None, offset=offset, limit=page_size)

        return JsonModel(
            code=200, 
            data={
                "items": [f.to_json() for f in files],
                "total": total,
                "page": page,
                "page_size": page_size
            }, 
            msg="success"
        ).to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


class UpdateDCFileEntity(BaseModel):
    file_id: uuid.UUID
    name: str | None = None
    group_ids: list[int] | None = None


# 更新双控清单文件信息
# 功能：管理员更新名称、用户组等信息
async def update_dc_file(request: Request, payload: UpdateDCFileEntity):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    if not payload.name and payload.group_ids is None:
        return JsonModel(code=300, data=None, msg="缺少更新内容").to_response()
    try:
        f = db_update_dc_file_info(payload.file_id, name=payload.name, group_ids=payload.group_ids)
        if not f:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="更新成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="更新失败").to_response()


# 删除双控清单文件（软删）
# 功能：管理员将文件标记为删除
async def delete_dc_file(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        success = db_update_dc_file_delete_state(file_id, True)
        if not success:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="删除成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="删除失败").to_response()


# 下载双控清单原始Excel
# 功能：管理员下载原始上传的Excel文件
async def download_dc_original(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        f = db_get_dc_file_by_id(file_id)
        if not f or f.is_delete:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        abs_path = to_abs_path(f.location)
        if not f.location or not os.path.exists(abs_path):
            return JsonModel(code=404, data=None, msg="文件未找到或已被清理").to_response()
        # 直接下发原始Excel
        return FileResponse(abs_path, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                            filename=f.name)
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="下载失败").to_response()
